﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Administration;
using ServBus.ListForm2010.Resources;
using ServBus.ListForm2010.Utility;
using ServBus.ListForm2010.TimerJob;
using ServBus.ListForm2010.Feature;

namespace ServBus.ListForm2010.License
{
    internal class Activations
    {
        private static DllResource Resource = new DllResource("ServBus.ListForm2010.Common.Resources|License");
        private SPFarm _farm;
        private SPWebApplication _webapp;
        private List<ActivationInfo> _activations;
        internal List<ActivationInfo> Infos
        {
            get { return _activations; }
        }

        private Activations()
        {
        }

        public Activations(SPFarm farm)
        {
            _farm = farm;
            InitActivation(farm.Properties);
        }

        public Activations(SPWebApplication webapp)
        {
            _webapp = webapp;
            InitActivation(webapp.Properties);
        }

        /// <summary>
        /// Read from farm or webapp
        /// </summary>
        /// <param name="hashTable"></param>
        private void InitActivation(Hashtable hashTable)
        {
            string base64 = Convert.ToString(hashTable[Constants.ACTIVATION_PROPERTY_KEY]);
            try
            {
                IFormatter formatter = new BinaryFormatter();
                using (MemoryStream stream = new MemoryStream(Convert.FromBase64String(base64)))
                {
                    _activations = formatter.Deserialize(stream) as List<ActivationInfo>;
                }
            }
            catch
            {
                _activations = new List<ActivationInfo>();
            }
        }

        /// <summary>
        /// Save to farm or webapp
        /// </summary>
        public void PersistActivation()
        {
            string updatedTime = DateTime.Now.ToString();
            string base64 = null;
            using (MemoryStream stream = new MemoryStream())
            {
                IFormatter formatter = new BinaryFormatter();
                formatter.Serialize(stream, _activations);
                stream.Seek(0, SeekOrigin.Begin);
                byte[] buffer = new byte[stream.Length];
                stream.Read(buffer, 0, buffer.Length);
                base64 = Convert.ToBase64String(buffer);
            }

            ListFormWS.FarmUpdate s = new ListFormWS.FarmUpdate();
            s.Url = SPAdministrationWebApplication.Local.Sites[0].Url + "/_layouts/ListForm2010/ListFormWS.asmx";
            s.UseDefaultCredentials = true;
            if (_farm != null)
            {
                s.updateFarmProperties(base64, Constants.ACTIVATION_PROPERTY_KEY);
            }
            else
            {
                s.updateAppProperties(base64, Constants.ACTIVATION_PROPERTY_KEY, _webapp.Id);
            }
        }

        

        /// <summary>
        /// Add activation to activation list
        /// </summary>
        /// <param name="activation"></param>
        /// <returns>if already more than limit, return false</returns>
        public bool AddActivation(ActivationInfo activation)
        {
            if (GetEffectiveCount() >= LicenseContainer.ProductLicense.ActivationLimit)
                return false;
            _activations.Add(activation);
            return true;
        } 

        /// <summary>
        /// Remove activation from activation list
        /// </summary>
        /// <param name="web"></param>
        public void DeleteActivation(Guid webId)
        {
            for (int i = _activations.Count - 1; i >= 0; i--)
            {
                if (_activations[i].WebId == webId)
                    _activations.RemoveAt(i);
            }
        }

        /// <summary>
        /// Effective activation count
        /// </summary>
        /// <returns></returns>
        public int GetEffectiveCount()
        {
            int count = 0;
            foreach (ActivationInfo activation in _activations)
            {
                if (activation.Type != ActivationType.Failed)
                    count++;
            }
            return count;
        }

        /// <summary>
        /// Is License valid
        /// </summary>
        /// <returns></returns>
        public bool IsLicenseValid(out string errorMsg)
        {
            errorMsg = null;
            // 1. Check license
            if (LicenseContainer.IsLicenseEmpty || !LicenseContainer.IsLicenseValid)
            {
                errorMsg = Resource.GetString("No_Valid");
                return false;
            }

            // 2. Check Expiration Date
            if (LicenseContainer.IsLicenseExpire)
            {
                errorMsg = Resource.GetString("Expired");
                return false;
            }
            return true;
        }

        /// <summary>
        /// Is totally valid
        /// </summary>
        /// <returns></returns>
        public bool IsValid(bool adding, out string errorMsg)
        {
            errorMsg = null;
            // 1. Check license
            if (LicenseContainer.IsLicenseEmpty || !LicenseContainer.IsLicenseValid)
            {
                errorMsg = Resource.GetString("No_Valid");
                return false;
            }

            // 2. Check Expiration Date
            if (LicenseContainer.IsLicenseExpire)
            {
                errorMsg = Resource.GetString("Expired");
                return false;
            }

            // 3. Check Server
            License license = LicenseContainer.ProductLicense;
            if (!license.IsValidServer(SPServer.Local.Id))
            {
                errorMsg = Resource.GetString("Server_Not_Registed");
                return false;
            }

            // 4. Check WebApp Activation
            if (_webapp != null)
            {
                if (!_webapp.Properties.ContainsKey(Constants.WEBAPP_ACTIVATINO_FLAG))
                {
                    errorMsg = Resource.GetString("WebApp_Not_Activated");
                    return false;
                }
            }

            // 5. Check Activation Limit
            int effectiveCount = (_webapp == null) ? GetEffectiveCount() : (new Activations(_webapp.Farm)).GetEffectiveCount();
            if ((effectiveCount > license.ActivationLimit && !adding) ||
                (effectiveCount >= license.ActivationLimit && adding))
            {
                errorMsg = Resource.GetString("Limit_Exceeded");
                return false;
            }

            return true;
        }

        /// <summary>
        /// Is valid to specific web
        /// </summary>
        /// <param name="web"></param>
        /// <returns></returns>
        public bool IsActivationValid(SPWeb web, out string errorMsg)
        {
            if (!IsValid(false, out errorMsg))
                return false;
            ActivationInfo activation = _activations.Where<ActivationInfo>(info => info.WebId == web.ID && info.Type != ActivationType.Failed).FirstOrDefault();
            if (activation == null)
            {
                //sunny 2012/10/29
                errorMsg = Resource.GetString("Site_Not_Activated");
                return false;
                //return true;
            }
            if (activation.Type == ActivationType.Waiting && activation.ActivatedTime.AddHours(1) < DateTime.Now)
            {
                errorMsg = Resource.GetString("Activated_Problem");
                return false;
            }
            return true;
        }

        /// <summary>
        /// Is valid to specific web ,sunny 2012/10/29
        /// </summary>
        /// <param name="web"></param>
        /// <returns></returns>
        public bool IsActivationValid_feature(SPWeb web, out string errorMsg)
        {
            if (!IsValid(false, out errorMsg))
                return false;
            ActivationInfo activation = _activations.Where<ActivationInfo>(info => info.WebId == web.ID && info.Type != ActivationType.Failed).FirstOrDefault();
            if (activation == null)
            {
                return true;
            }
            if (activation.Type == ActivationType.Waiting && activation.ActivatedTime.AddHours(1) < DateTime.Now)
            {
                errorMsg = Resource.GetString("Activated_Problem");
                return false;
            }
            return true;
        }
        public static void ActivateWebApp(SPWebApplication webapp)
        {
            webapp.Properties[Constants.WEBAPP_ACTIVATINO_FLAG] = "1";
            webapp.Update();
            ResourceUtility.InstallToWebConfig(webapp);
            ClearAttDoclib.InstallToWebapp(webapp);
        }

        public static void DeactivateWebApp(SPWebApplication webapp)
        {
            // Remove Timerjob
            ClearAttDoclib.RemoveFromWebapp(webapp);

            // Deactivate Features under webapp
            Activations actWebapp = new Activations(webapp);
            foreach (ActivationInfo actInfo in actWebapp.Infos)
            {
                SPSecurity.RunWithElevatedPrivileges(() =>
                {
                    using (SPSite site = new SPSite(actInfo.SiteId))
                    {
                        using (SPWeb web = site.OpenWeb(actInfo.WebId))
                        {
                            try
                            {
                                web.Features.Remove(Constants.WEB_FEATURE_ID, true);
                            }
                            catch
                            {
                            }
                            // Ensure the doclib is removed
                            WebFeatureReceiver.RemoveDoclib(web, true);
                        }
                    }
                });
            }

            // Remove properties
            if (webapp.Properties.ContainsKey(Constants.WEBAPP_ACTIVATINO_FLAG))
                webapp.Properties.Remove(Constants.WEBAPP_ACTIVATINO_FLAG);
            if (webapp.Properties.ContainsKey(Constants.ACTIVATION_PROPERTY_KEY))
                webapp.Properties.Remove(Constants.ACTIVATION_PROPERTY_KEY);
            webapp.Update();
            ResourceUtility.RemoveFromWebConfig(webapp);
        }

        public static bool IsActivatedOnWebApp(SPWebApplication webapp)
        {
            return webapp.Properties.ContainsKey(Constants.WEBAPP_ACTIVATINO_FLAG);
        }

        public static void SyncFromWebApps()
        {
            List<ActivationInfo> actInfos = new List<ActivationInfo>();
            Activations farmAct = new Activations(SPFarm.Local);
            List<Activations> webAppActs = new List<Activations>();

            foreach (SPWebApplication webapp in SPWebService.ContentService.WebApplications)
            {
                if (webapp.IsAdministrationWebApplication)
                    continue;
                if (!webapp.Properties.ContainsKey(Constants.ACTIVATION_PROPERTY_KEY))
                    continue;

                Activations webAppAct = new Activations(webapp);
                webAppActs.Add(webAppAct);
                actInfos.AddRange(webAppAct._activations);
            }

            // Sort
            actInfos.Sort();

            // Find Delete
            for (int i = farmAct._activations.Count - 1; i >= 0; i--)
            {
                var acts = actInfos.Where<ActivationInfo>(act => act.WebId == farmAct._activations[i].WebId && act.Type == ActivationType.Activated);
                if (acts.Count() == 0)
                {
                    // Remove from farm
                    farmAct._activations.RemoveAt(i);
                }
            }

            // Activate new infos
            int limit = 0;
            if (LicenseContainer.ProductLicense != null)
                limit = LicenseContainer.ProductLicense.ActivationLimit;
            foreach (ActivationInfo act in actInfos)
            {
                if (act.Type == ActivationType.Waiting)
                {
                    if (farmAct._activations.Count < limit)
                    {
                        // Activate
                        act.Type = ActivationType.Activated;
                        farmAct._activations.Add(act);
                    }
                    else
                    {
                        // Activate Failed
                        act.Type = ActivationType.Failed;
                    }
                }
            }

            // Persist
            farmAct.PersistActivation();
            foreach (Activations webappAct in webAppActs)
            {
                webappAct.PersistActivation();
            }
        }
    }
}
